/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_SPLITTER)

#include "umc_avs_splitter.h"
#include "umc_avs_splitter_buffer.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_dec_byte_stream.h"
#include "vm_time.h"

namespace UMC
{

enum
{
    AVS_SPLITTER_TIME_TO_SLEEP  = 5
};

AVSSplitter::AVSSplitter(void)
{
    m_bQuit = false;

    m_pVideoBuffer = NULL;
    m_pDataReader = NULL;

} // AVSSplitter::AVSSplitter(void)

AVSSplitter::~AVSSplitter(void)
{
    Close();

} // AVSSplitter::~AVSSplitter(void)

Status AVSSplitter::Close(void)
{
    // delete & close all resources
    m_bQuit = true;
    if (m_hThread.IsValid())
    {
        m_hThread.Wait();
        m_hThread.Close();
    }

    if (m_pVideoBuffer)
    {
        delete m_pVideoBuffer;
    }

    // reset all resources
    m_bQuit = false;

    m_pVideoBuffer = NULL;
    m_pDataReader = NULL;
    if(m_info.m_ppTrackInfo)
    {
        if(m_info.m_ppTrackInfo[0])
        {
            if(m_info.m_ppTrackInfo[0]->m_pStreamInfo)
            {
                delete m_info.m_ppTrackInfo[0]->m_pStreamInfo;
                m_info.m_ppTrackInfo[0]->m_pStreamInfo = NULL;
            }

            delete m_info.m_ppTrackInfo[0];
            m_info.m_ppTrackInfo[0] = NULL;
        }

        delete m_info.m_ppTrackInfo;
        m_info.m_ppTrackInfo = NULL;
    }

    // here we should call the parent's method,
    // but the parent class does not have this implementation
    // Splitter::Close();

    return UMC_OK;

} // Status AVSSplitter::Close(void)

Status AVSSplitter::Init(SplitterParams &initParams)
{
    Status umcRes;
    MediaBufferParams bufferParams;

    // check incompatible flag(s)
    if (0 == (initParams.m_lFlags & VIDEO_SPLITTER))
        return UMC_ERR_INVALID_STREAM;
    //for simple_player
    //if (initParams.m_lFlags & AUDIO_SPLITTER)
    //    return UMC_ERR_INVALID_STREAM;
    if (initParams.m_lFlags & FLAG_VSPL_NO_INTERNAL_THREAD)
        return UMC_ERR_INVALID_STREAM;
    // check parameter(s)
    if (NULL == initParams.m_pDataReader)
        return UMC_ERR_NULL_PTR;

    // close all resources before initialization
    Close();

    // here we should call the parent's method,
    // but the parent class does not have this implementation
    // umcRes = Splitter::Init(initParams);
    // if (UMC_OK != umcRes)
    //     return umcRes;

    m_pDataReader = initParams.m_pDataReader;

    // allocate resources

    // allocate sample buffer
    m_pVideoBuffer = new AVSSampleBuffer();
    if (NULL == m_pVideoBuffer)
        return UMC_ERR_ALLOC;
    umcRes = m_pVideoBuffer->Init(&bufferParams);
    if (UMC_OK != umcRes)
        return umcRes;

    // create an internal thread
    umcRes = m_hThread.Create(AVSSplitter::InternalThreadRoutine, this);
    if (UMC_OK != umcRes)
        return umcRes;

    // wait for first sequence header
    umcRes = PrepareStreamInfo();
    if (UMC_OK != umcRes)
        return umcRes;

    // return specific information
    if ((initParams.m_lFlags & FLAG_VSPL_VIDEO_HEADER_REQ) ||
        (initParams.m_lFlags & FLAG_VSPL_VIDEO_FRAME_REQ))
    {
        /* DEBUG: need to develop */
    }

    return UMC_OK;

} // Status AVSSplitter::Init(SplitterParams &initParams)

Status AVSSplitter::GetNextData(MediaData *pDst, Ipp32u)
{
    Status umcRes;

    // check error(s)
    if (NULL == pDst)
        return UMC_ERR_NULL_PTR;
    if (false == m_pVideoBuffer)
        return UMC_ERR_NOT_INITIALIZED;

    // skip the previous sample
    if (m_latestSample.GetBufferPointer())
    {
        // move data pointer and skip used source
        m_latestSample.MoveDataPointer((Ipp32s) m_latestSample.GetDataSize());
        m_pVideoBuffer->UnLockOutputBuffer(&m_latestSample);
        m_latestSample.SetBufferPointer(NULL, 0);
    }

    // lock the next portion of source data
    umcRes = m_pVideoBuffer->LockOutputBuffer(&m_latestSample);
    if (UMC_OK != umcRes)
        return umcRes;

    // provide buffer to caller
    pDst->SetBufferPointer((Ipp8u *) m_latestSample.GetBufferPointer(), m_latestSample.GetDataSize());
    pDst->SetDataSize(m_latestSample.GetDataSize());
    pDst->SetTime(-1.0);

    return UMC_OK;

} // Status AVSSplitter::GetNextData(MediaData *pDst, Ipp32u)

Status AVSSplitter::GetInfo(SplitterInfo **pSplitterInfo)
{
    // copy pointer to info
    *pSplitterInfo = &m_info;
    return UMC_OK;
} // Status AVSSplitter::GetInfo(SplitterInfo *pSplitterInfo)

Status AVSSplitter::PrepareStreamInfo(void)
{
    Status umcRes;

    // lock the first portion of source data
    do
    {
        umcRes = m_pVideoBuffer->LockOutputBuffer(&m_latestSample);
        if (UMC_OK == umcRes)
            break;

        vm_time_sleep(AVS_SPLITTER_TIME_TO_SLEEP);

    } while (UMC_ERR_NOT_ENOUGH_DATA == umcRes);
    if (UMC_OK != umcRes)
        return umcRes;

    // first header should be a sequence header
    umcRes = UpdateSplitterInfo((Ipp8u *) m_latestSample.GetDataPointer(),
                                 m_latestSample.GetDataSize());
    if (UMC_OK != umcRes)
        return umcRes;

    // we need to unlock source to avoid it's dropping
    m_pVideoBuffer->UnLockOutputBuffer(&m_latestSample);
    // and set data size to 0 to avoid it's skipping
    m_latestSample.SetBufferPointer(NULL, 0);

    return UMC_OK;

} // Status AVSSplitter::PrepareStreamInfo(void)
static
struct AVSAspectRatio
{
    Ipp32u horz;
    Ipp32u vert;

} AVSAspectRatios[] =
{
    {1, 1},
    {1, 1},
    {4, 3},
    {16, 9},
    {221, 100},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1},
    {1, 1}
};

Status AVSSplitter::UpdateSplitterInfo(Ipp8u *pSource, size_t srcSize)
{
    AVS_SEQUENCE_HEADER seqHeader;
    Status umcRes;

    // decode sequence header
    umcRes = DecodeAVSSequenceHeader(&seqHeader, pSource, srcSize);
    if (UMC_OK != umcRes)
        return umcRes;

    // set default information
    m_info.m_splitter_flags = VIDEO_SPLITTER;
    m_info.m_SystemType = AVS_PURE_VIDEO_STREAM;
    m_info.m_nOfTracks = 1;

    // fill information into the structure
    VideoStreamInfo info;
    info.clip_info.width = seqHeader.horizontal_size;
    info.clip_info.height = seqHeader.vertical_size;
    info.bitrate = (seqHeader.bit_rate_upper << 18) + seqHeader.bit_rate_lower;
    info.aspect_ratio_width = AVSAspectRatios[seqHeader.aspect_ratio].horz;
    info.aspect_ratio_height = AVSAspectRatios[seqHeader.aspect_ratio].vert;
    info.framerate = seqHeader.frame_rate;
    info.interlace_type = (seqHeader.progressive_sequence) ?
                          (PROGRESSIVE) :
                          (INTERLEAVED_TOP_FIELD_FIRST);
    info.stream_type = AVS_VIDEO;
    info.streamPID = 0;

    m_info.m_ppTrackInfo = new TrackInfo*[1];
    if(m_info.m_ppTrackInfo == NULL)
    {
        Close();
        return UMC_ERR_ALLOC;
    }

    m_info.m_ppTrackInfo[0] = new TrackInfo;
    if(m_info.m_ppTrackInfo[0] == NULL)
    {
        Close();
        return UMC_ERR_ALLOC;
    }

    m_info.m_ppTrackInfo[0]->m_Type = TRACK_AVS;
    m_info.m_ppTrackInfo[0]->m_isSelected = 1;
    m_info.m_ppTrackInfo[0]->m_pStreamInfo = new VideoStreamInfo;
    if(m_info.m_ppTrackInfo[0]->m_pStreamInfo == NULL)
    {
        Close();
        return UMC_ERR_ALLOC;
    }

    VideoStreamInfo* video_info = (VideoStreamInfo *)m_info.m_ppTrackInfo[0]->m_pStreamInfo;
    *video_info= info;

    return UMC_OK;

} // Status AVSSplitter::UpdateSplitterInfo(Ipp8u *pSource, size_t srcSize)

} // namespace UMC

#endif // #if defined(UMC_ENABLE_AVS_SPLITTER)
